package cn.skyquant.quant4j.service.boot.model.forex;

import cn.skyquant.quant4j.api.dto.ResultDTO;
import cn.skyquant.quant4j.api.enums.BorkerType;
import cn.skyquant.quant4j.api.enums.ForexConfigType;
import cn.skyquant.quant4j.api.forex.ForexConfigDTO;
import cn.skyquant.quant4j.api.forex.ForexConfigSeriesDTO;
import cn.skyquant.quant4j.api.forex.ForexConfigSymbolDTO;
import cn.skyquant.quant4j.api.forex.ForexService;
import cn.skyquant.quant4j.sdk.util.bean.BeanTools;
import cn.skyquant.quant4j.service.boot.model.goods.Goods;
import cn.skyquant.quant4j.service.boot.model.goods.GoodsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ForexServiceImpl implements ForexService {

    public static final ResultDTO error1 = ResultDTO.error("同名的配置已经存在");
    public static final ResultDTO error2 = ResultDTO.error("找不到配置项");
    @Autowired
    ForexConfigRepository forexConfigRepository;
    @Autowired
    ForexConfigSymbolRepository forexConfigSymbolRepository;
    @Autowired
    ForexConfigSeriesRepository forexConfigSeriesRepository;
    @Autowired
    GoodsRepository goodsRepository;


    @Override
    public ResultDTO<List<ForexConfigDTO>> findConfigByTypeAndBorkerType(ForexConfigType type, BorkerType borkerType) {
        List<ForexConfig> list = forexConfigRepository.findByTypeAndBorkerType(type, borkerType, ForexConfigRepository.sort_by_id);
        List<ForexConfigDTO> dtoList = convertForexConfigDosToDTOs(list);
        return ResultDTO.success(dtoList);
    }

    @Override
    public ResultDTO<ForexConfigDTO> getConfigById(Integer id) {
        Optional<ForexConfig> forexConfigOptional = forexConfigRepository.findById(id);
        if(forexConfigOptional.isPresent()){
            ForexConfigDTO forexConfigDTO = convertForexConfigDoToDTO(forexConfigOptional.get());
            return ResultDTO.success(forexConfigDTO);
        }
        return error2;
    }

    @Transactional
    @Override
    public ResultDTO<Void> addConfig(String name, BorkerType borkerType, ForexConfigType type) {
        Optional<ForexConfig> optionalForexConfig = forexConfigRepository.findByNameAndTypeAndBorkerType(name, type, borkerType);
        if (optionalForexConfig.isPresent()) {
            return error1;
        } else {
            ForexConfig newConfig = new ForexConfig(name, type, borkerType);
            ForexConfig db = forexConfigRepository.save(newConfig);
            if (db.id != null) {
                return ResultDTO.success;
            } else {
                return ResultDTO.error;
            }
        }
    }

    @Transactional
    @Override
    public ResultDTO<Void> deleteConfig(Integer id) {
        forexConfigSeriesRepository.deleteByConfigId(id);
        forexConfigSymbolRepository.deleteByConfigId(id);
        forexConfigRepository.deleteById(id);
        return ResultDTO.success;
    }

    @Transactional
    @Override
    public ResultDTO<Void> copyConfig(Integer id, String newName) {
        Optional<ForexConfig> optionalForexConfig = forexConfigRepository.findById(id);
        if (optionalForexConfig.isPresent()) {
            ForexConfig forexConfig = optionalForexConfig.get();
            Optional<ForexConfig> same = forexConfigRepository.findByNameAndTypeAndBorkerType(newName, forexConfig.type, forexConfig.borkerType);
            if (same.isPresent()) {
                return error1;
            }else {
                ForexConfig newForexConfig = forexConfig.clone(newName);
                ForexConfig dbNew = forexConfigRepository.save(newForexConfig);
                if(dbNew.id!=null){
                    List<ForexConfigSeries> seriesList = forexConfigSeriesRepository.findByConfig(forexConfig,ForexConfigSeriesRepository.forexConfigSeriesSort);
                    List<ForexConfigSymbol> symbolList = forexConfigSymbolRepository.findByConfig(forexConfig,ForexConfigSymbolRepository.forexConfigSymbolSort);
                    seriesList.stream().map(x->x.clone(dbNew)).forEach(x->forexConfigSeriesRepository.save(x));
                    symbolList.stream().map(x->x.clone(dbNew)).forEach(x->forexConfigSymbolRepository.save(x));
                }
                return ResultDTO.success;
            }
        }else {
            return error2;
        }
    }

    @Transactional
    @Override
    public ResultDTO<Void> updateConfig(Integer id, String newName) {
        Optional<ForexConfig> optionalForexConfig = forexConfigRepository.findById(id);
        if (optionalForexConfig.isPresent()) {
            ForexConfig forexConfig = optionalForexConfig.get();
            Optional<ForexConfig> same = forexConfigRepository.findByNameAndTypeAndBorkerType(newName, forexConfig.type, forexConfig.borkerType);
            if (same.isPresent()) {
                return error1;
            } else {
                forexConfig.name = newName;
                return ResultDTO.success;
            }
        } else {
            return error2;
        }
    }

    @Transactional
    @Override
    public ResultDTO<Void> updateConfigSymbol(Integer configId, List<ForexConfigSymbolDTO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return ResultDTO.error1;
        }
        Optional<ForexConfig> optionalForexConfig = forexConfigRepository.findById(configId);
        if (!optionalForexConfig.isPresent()) {
            return error2;
        }

        ForexConfig forexConfig = optionalForexConfig.get();
        forexConfigSymbolRepository.deleteByConfigId(forexConfig.id);
        list.forEach(x->{
            ForexConfigSymbol forexConfigSymbol = convertForexConfigSymbolDtoToDo(x);
            forexConfigSymbol.config = forexConfig;
            Optional<Goods> goodsOptional = goodsRepository.getBySymbolAndBorkerType(forexConfigSymbol.symbol,forexConfig.borkerType);
            if(goodsOptional.isPresent()){
                forexConfigSymbolRepository.save(forexConfigSymbol);
            }
        });
        return ResultDTO.success;
    }

    @Override
    public ResultDTO<List<ForexConfigSymbolDTO>> findConfigSymbol(Integer configId) {
        Optional<ForexConfig> optionalForexConfig = forexConfigRepository.findById(configId);
        if (!optionalForexConfig.isPresent()) {
            return error2;
        }

        ForexConfig forexConfig = optionalForexConfig.get();
        List<ForexConfigSymbol> list = forexConfigSymbolRepository.findByConfig(forexConfig,ForexConfigSymbolRepository.forexConfigSymbolSort);
        List<ForexConfigSymbolDTO> forexConfigSymbolDTOList = convertForexConfigSymbolDosToDtos(list);
        return ResultDTO.success(forexConfigSymbolDTOList);
    }

    @Transactional
    @Override
    public ResultDTO<Void> updateConfigSeries(Integer configId, List<ForexConfigSeriesDTO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return ResultDTO.error1;
        }

        Optional<ForexConfig> optionalForexConfig = forexConfigRepository.findById(configId);
        if (!optionalForexConfig.isPresent()) {
            return error2;
        }
        ForexConfig forexConfig = optionalForexConfig.get();
        forexConfigSeriesRepository.deleteByConfigId(forexConfig.id);
        list.forEach(x->{
            ForexConfigSeries forexConfigSeries = convertForexConfigSeriesDtoToDo(x);
            forexConfigSeries.config = forexConfig;
            forexConfigSeriesRepository.save(forexConfigSeries);
        });
        return ResultDTO.success;
    }

    @Override
    public ResultDTO<List<ForexConfigSeriesDTO>> findConfigSeries(Integer configId) {
        Optional<ForexConfig> optionalForexConfig = forexConfigRepository.findById(configId);
        if (!optionalForexConfig.isPresent()) {
            return error2;
        }

        ForexConfig forexConfig = optionalForexConfig.get();
        List<ForexConfigSeries> list = forexConfigSeriesRepository.findByConfig(forexConfig,ForexConfigSeriesRepository.forexConfigSeriesSort);
        List<ForexConfigSeriesDTO> forexConfigSeriesDTOList = convertForexConfigSeriesDosToDtos(list);
        return ResultDTO.success(forexConfigSeriesDTOList);
    }


    private static List<ForexConfigDTO> convertForexConfigDosToDTOs(List<ForexConfig> forexConfigs) {
        return forexConfigs.stream().map(x -> convertForexConfigDoToDTO(x)).collect(Collectors.toList());
    }

    private static ForexConfigDTO convertForexConfigDoToDTO(ForexConfig forexConfig) {
        ForexConfigDTO forexConfigDTO = new ForexConfigDTO();
        BeanTools.copyPublicFields(forexConfig, forexConfigDTO);
        return forexConfigDTO;
    }

    private static ForexConfigSymbol convertForexConfigSymbolDtoToDo(ForexConfigSymbolDTO dto) {
        ForexConfigSymbol forexConfigSymbol = new ForexConfigSymbol();
        BeanTools.copyPublicFields(dto,forexConfigSymbol);
        return forexConfigSymbol;
    }

    private static List<ForexConfigSymbolDTO> convertForexConfigSymbolDosToDtos(List<ForexConfigSymbol> list) {
        return list.stream().map(x->convertForexConfigSymbolDoToDto(x)).collect(Collectors.toList());
    }

    private static ForexConfigSymbolDTO convertForexConfigSymbolDoToDto(ForexConfigSymbol forexConfigSymbol) {
        ForexConfigSymbolDTO forexConfigSymbolDTO = new ForexConfigSymbolDTO();
        BeanTools.copyPublicFields(forexConfigSymbol,forexConfigSymbolDTO);
        return forexConfigSymbolDTO;
    }

    private static ForexConfigSeries convertForexConfigSeriesDtoToDo(ForexConfigSeriesDTO dto) {
        ForexConfigSeries forexConfigSymbol = new ForexConfigSeries();
        BeanTools.copyPublicFields(dto,forexConfigSymbol);
        return forexConfigSymbol;
    }

    private static List<ForexConfigSeriesDTO> convertForexConfigSeriesDosToDtos(List<ForexConfigSeries> list) {
        return list.stream().map(x->convertForexConfigSeriesDoToDto(x)).collect(Collectors.toList());
    }

    private static ForexConfigSeriesDTO convertForexConfigSeriesDoToDto(ForexConfigSeries forexConfigSeries) {
        ForexConfigSeriesDTO forexConfigSeriesDTO = new ForexConfigSeriesDTO();
        BeanTools.copyPublicFields(forexConfigSeries,forexConfigSeriesDTO);
        return forexConfigSeriesDTO;
    }

}
